作者:陈广 日期:2019-6-14
掌握了 HID 报告描述符后,我们就可以动手实现一个鼠标了。当然,首先必须要先有一块开发板。我用的是 STM32,原因是 STM32 性能高,价钱便宜,最重要的是学习资料丰富,现在又得加一条,配套软件功能强大。STM32CubeMx 的出现使得 STM32 系列单片机编程的难度下降了几个层级。开发板必须支持 USB,我使用的芯片是 STM32F103RCT6,原来是用它做键盘的,支持 USB 2.0。开发板应当至少带有 6 个按钮,我手头有这样的板子,就是键盘 PCB,但不能拿来写文章啊。总不能让通过这篇文章学习的同学去打一块键盘吧。还好,我手上有一块 STM32F103RCT6 的评估板,网上随便可以买到,价钱便宜。缺点是没有按键,那只能通过定时器来演示了,效果会差一些。STM32开发板我肯定会设计一款,上课要用,不过不是现在。最完美的情况是自己设计并打造一块带 9 个按键的 STM32 开发板,然后学习这篇文章,但那需要较有钱才行。先放设备图片:
购买链接:https://item.taobao.com/item.htm?spm=a1z09.2.0.0.2ee92e8dxp3NCM&id=571177356496&_u=dhtvhlo156b
这只是我的购买链接,30 大洋,很便宜,这块板网上到处都是,可自行选择购买。(我写这篇文章,在调试的时候,突然发现这块板子挂掉了,杯具!)
另外虽然这块开发板可以使用串口一键下载,但用起来不是太方便,所以最好买一个 ST-LINK,如图右上角所示。使用它可以直接在 Keil 软件中烧写程序,十来块钱就可买到,以后设计其它的 STM 开发板只需接3条线出来就可以使用了。
以往我是不敢开 STM32 单片机这门课的,因为光一个 GPIO 的配置就足够复杂,让人望而却步,并不适合高职学生实习。但意法公司出了 STM32CUBEMX 后,改变了这种情况,它使用图形化的方式对单片机进行配置,并自动生成代码,让我们可以专注于业务逻辑,将 STM32 的学习难度直接降低几个层级,甚至于都不需要读芯片数据手册就可以完成程序的开发。要知道,学 STM32 最大的难点就在于读数据手册,大部分是全英文的。现在,我终于可以开设这门课程了。
STM32CUBEMX 和 Keil 的安装我不再赘述,B 站上有太多的资料,看看就懂了,我们直接进入主题。
打开 STM32CUBDMX,单击屏幕中央【ACCESS TO MCU SELECTOR】按钮,在弹出的窗口中搜索并选中【STM32F103RCTx】,有两种封装:LQFP64 和 WLCSP64,根据实际情况选择。选好后,项目也创建好了。
首先配置外部晶振,在左侧栏中展开【System Core】项,选中【RCC】,在中间【RCC Mode and Configuration】窗口下的【High Speed Clock (HSE)】组合框中选择【Crystal/Ceramic Resonator】,选中这项,意味着我们使用了外部晶振,开发板上至少有一块 8M 的外部晶振。如下图所示:
接下来,选择左边栏的【SYS】项,在【SYS Mode and Configuartion】窗口中【Debug】组合框中选中【Serial Wire】项,以选择 ST-LINK 方式烧写程序。这个需要确认你所使用的开发板已经引出【SWCLK】和【SWDIO】两个引脚。
首先使能 USB,展开左侧栏的【Connectivity】项,选中其中的【USB】项,在【USB Mode and Configuration】窗口中勾选【Device(FS)】项,如下图所示:
接下来配置 HID,展开左侧栏【Middleware】项,选中其中的【USB_DEVICE】项,在【USB_DEVICE Mode and Configuration】窗口下的【Class For FS IP】项中选中【Human Interface Device Class(HID)】项。在下方【Configuration】窗口中你可以将 VID 和 PID 更改为喜欢的数字,将【MANUFACTURER_STRING】更改为你喜欢的内容,如下图所示:
接下来将 USB 中断优先级改低点。展开侧边栏【System Core】项,选中【NVIC】,在右边【NVIC Mode and Configuration】窗口中将【USB low priority or CAN RX0 interrupts】项的优先级改为5
。如下图所示:
下面我们来配置时钟,在屏幕上方选项卡中选中【Clock Configuration】项。并按照下图所示配置时钟:
注意,先使能 USB 才能配置时钟中的【USB Prescaler】项,所以把时钟配置放在后面。
选中窗体上方【Project Manager】选项卡,在【Project】栏中的【Toolchain/IDE】组合框选择【MDK-ARM】项,【Min Version】选择【V5.27】,表明使用的是 Keil 的最新版。
打开【Code Generator】栏,勾选其中的【Generate peripheral initialzation as a pair of '.c/ch' files per peripheral】项。然后单击【GENEREATE CODE】按钮生成代码。
在生成项目的文件夹中打开【MDK-ARM】文件夹,双击里面后缀名为.uvprojx
的文件(图标为 Keil 的文件),打开 CubeMx 自动生成的项目。
将 ST-Link 连至开发板,只需连三条线:SWCLK、SWDIO、GND。然后开发板连上位机,有两个 USB 口,有一个口附近是 CH340 芯片,这个是串口,连接另一个 USB。然后在 Keil 中的工具栏上点魔术棒按钮(Options for Target),选择【Debug】选项卡,在【Use】组合框中选择【ST-Link Debugger】项,如下图所示:
然后点击【Use】组合框右侧的【Settings】按钮打开【Target Driver Setup】窗口,如果在【SW Device】面板下的【SWDIO】栏中显示的是No target connected
,则表明 ST-Link 连接有问题,或设备损坏。如果上面为设备名称,则说明 ST-Link 已经可以使用。很不幸,我在文章开头给大家介绍的开发板就卡这了,居然坏掉了,太坑了!没办法,只能拿我做的键盘来烧程序了。注意,随便买一块 STM32F103RCT6 开发板,只要连接了 USB 口,都可以使用我将要写的程序。
STM32CubeMX 自动生成的 HID 默认为一个鼠标设备,我们可以在【Middlewares/USB_Device_Library】下的【usbd_hid.c】文件下查找HID_MOUSE_ReportDesc
定义,如下图所示:
注意,此报告描述符和我们之前讲解的 HID Descriptor Tool 自带的不一样,此报告描述符数据长度为 4 个字节,多一个字节用于存放滚轮数据,另外还声明了一个上位机传给鼠标的数据,具体作用我就不研究了。我们就直接使用这个更为正规的鼠标报告描述符来写程序吧。
要在程序中使用 HID 设备,需要增加一个外部声明。我们首先编译一下自动生成的程序,以方便通过展开【Application/User】下的【main.c】文件查看相应的头文件。编译后展开【main.c】,找到并打开【usb_device.h】文件,添加一句代码:
extern USBD_HandleTypeDef hUsbDeviceFS;
添加位置如下图所示:
添加这句代码后,在【main.c】文件中就可以使用hUsbDeviceFS
了。
打开【main.c】文件,使用以下代码引入 HID 库:
#include "usbd_hid.h"
插入位置应当在/* USER CODE BEGIN Includes */
和/* USER CODE END Includes */
之间。
最后在 main 函数中插入主逻辑代码:
/* USER CODE BEGIN 2 */
int8_t report[4]={0,0,0,0}; //4 字节长度遵照鼠标报告描述符指示
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(2000);
//单击鼠标右键
report[2]=0;
report[0]=2; //右键按下
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(100);
report[0]=0; //右键弹起
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(2000);
//鼠标向左移动50个像素
report[1]=-50;
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(2000);
//单击鼠标左键
report[1]=0;
report[0]=1; //左键按下
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(100);
report[0]=1; //左键弹起
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(2000);
//鼠标向下移动50个像素
report[2]=50;
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(2000);
//鼠标向右移动50个像素
report[2]=0;
report[1]=50;
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
HAL_Delay(2000);
//鼠标向上移动50个像素
report[1]=0;
report[2]=-50;
USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
需要注意的是电脑端鼠标需要按下并弹起,才算一个完整的单击,所以单片机这端要完成一个单击操作,需发送两次数据。
USB 设备好像插麻烦,烧完程序后,上位机不认 USB 设备,非得断电重启后才认。需要注意的是此程序让鼠标自动移动和单击,所以最好打开一个记事本,将鼠标放置在中央再启动开发板,观察鼠标的动作。
怎么样?写个鼠标程序还是很简单的吧,这在以前是完全不可想像的,下篇文章,我们来模拟一个键盘程序。
;